package com.xfatm.util.thread;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;

public class SafeThreadPool implements Runnable{
    //线程列表
    private final List<SafeThread> threads;
    //守护线程
    private Thread guardThread;
    //待执行任务队列
    private final BlockingQueue<Runnable> tasks;
    //超时时间
    private final long timeOut;
    //最大线程数
    private final int maxThreadNum;

    //同步线程相关
    private final BlockingQueue<Runnable> doneTasks;
    private int willExecuteNum;
    //超时任务
    private final BlockingQueue<Runnable> timeOutTasks;

    /**
     * @param timeOut 超时时间
     */
    public SafeThreadPool(long timeOut){
        this(timeOut,1);
    }

    /**
     * @param timeOut 超时时间
     * @param maxThreadNum 最大线程数
     */
    public SafeThreadPool(long timeOut, int maxThreadNum){
        this.timeOut=timeOut;
        threads=new ArrayList<>();
        this.maxThreadNum=maxThreadNum;
        tasks=new LinkedBlockingQueue<>();
        doneTasks =new LinkedBlockingQueue<>();
        timeOutTasks =new LinkedBlockingQueue<>();
    }

    /**
     * 执行任务
     * @param r
     */
    public void execute(Runnable r){
        if(guardThread==null){
            fullThreads();
            initGuradeThread();
        }
        try {
            willExecuteNum++;
            tasks.put(r);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    /**
     * 等待所有任务执行完毕，否则线程阻塞
     */
    public void waitAllDone(){
        while(willExecuteNum>0){
            willExecuteNum--;
            try {
                doneTasks.take();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

    /**
     * 等待所有任务都在运行状态，否则线程阻塞
     */
    public void waitAllDoing(){
        while(willExecuteNum-maxThreadNum>0){
            willExecuteNum--;
            try {
                doneTasks.take();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

    @Override
    public void run() {
        while(true){
            //结束超时任务
            try {
                stopTimeOutThread();
                Thread.sleep(timeOut/2>100?timeOut/2:100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

    public BlockingQueue<Runnable> getTimeOutTasks() {
        return timeOutTasks;
    }

    //初始化守护线程
    private void initGuradeThread(){
        guardThread=new Thread(this);
        guardThread.start();
    }

    //停止超时线程
    private void stopTimeOutThread(){
        Iterator<SafeThread> it=threads.iterator();
        while(it.hasNext()){
            SafeThread st=it.next();
            if(st.isRunning()&&System.currentTimeMillis()-st.startTime>timeOut){
                System.out.println("强行结束线程");
                doneTasks.add(st.getRunningTask());
                timeOutTasks.add(st.getRunningTask());
                st.stop();
                it.remove();
            }
        }
        fullThreads();
    }

    //初始化空闲线程
    private void fullThreads(){
        int size=threads.size();
        for(int i=0;i+size<maxThreadNum;i++){
            SafeThread st=new SafeThread(tasks, doneTasks);
            st.start();
            threads.add(st);
        }
    }

    private class SafeThread extends Thread{
        private long startTime;
        private boolean isRunning;
        private final BlockingQueue<Runnable> tasks;
        private final BlockingQueue<Runnable> doneTasks;
        private Runnable runningTask;

        public SafeThread(BlockingQueue<Runnable> tasks,BlockingQueue<Runnable> doneTasks){
            this.tasks=tasks;
            this.doneTasks = doneTasks;
        }

        @Override
        public void run() {
            while (true){
                try {
                    Runnable task=tasks.take();
                    isRunning=true;
                    startTime=System.currentTimeMillis();
                    runningTask=task;
                    task.run();
                    isRunning=false;
                    runningTask=null;
                    doneTasks.put(task);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }

        public long getStartTime() {
            return startTime;
        }
        public boolean isRunning() {
            return isRunning;
        }

        public Runnable getRunningTask() {
            return runningTask;
        }
    }
}

